home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1997 August / Walnut Creek CDROM.7z / LISTINGS / V_13_04 / CHAPMAN2 / CHAPMAN2.ZIP / COMPLAIN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-02  |  9.6 KB  |  269 lines

  1. /* complain.cpp / error text lookup */
  2.  
  3. /* error messages in a program can be "keyed;" that is, they can have a */
  4. /* replacement key that allows us to look up the message and replace the */
  5. /* text. this module manages error message files, scanning them and storing */
  6. /* message offsets for quick lookup. users can edit the files to change */
  7. /* messages without patching the program. this allows easier */
  8. /* internationalization, too. of course, the "%" arguments must remain in */
  9. /* the same order so that printf() doesn't choke! that's the job of err_mgr */
  10. /* (q.v.). */
  11.  
  12. #include <stdio.h>
  13. #ifdef __ZTC__
  14. #include <io.h>                         /* fseek() */
  15. #endif
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include "utils.hpp"                    /* utility routines */
  19. #include "errmgr.hpp"                   /* err_mgr.warn() etc. */
  20. #include "complain.hpp"                 /* us */
  21.  
  22. /* in this implementation keys are stored in a singly linked list. a */
  23. /* hash table would be much faster, of course. */
  24.  
  25. class complain_ptr {                    /* for list of keys */
  26.     public:
  27.         complain_ptr(char *name,long offset,complain_ptr *head);
  28.         ~complain_ptr(void);
  29.         const char *errname(void) const { return _errname; }
  30.         int complaint_text(const char *filename,char *line,int linelen);
  31.         complain_ptr *next;             /* singly linked list */
  32.     private:
  33.         char *_errname;                 /* allocated by caller; we free */
  34.         long foffset;                   /* where to find its text in file */
  35. };
  36.  
  37. /*************************** class complaint_dict ***************************/
  38.  
  39. /* reads file, returns head of key list if all OK or 0 otherwise */
  40.  
  41. static complain_ptr *read_complaint_file(const char *filename);
  42.  
  43. /* searches list of keys; returns 0 if not found. */
  44.  
  45. static complain_ptr *key(complain_ptr *complain_table,const char *name);
  46.  
  47. complaint_dict::complaint_dict(const char *filename)
  48. {
  49.     /* read the complaint file and remember pointers to each error message */
  50.     /* found therein. */
  51.  
  52.     _filename = newstring(filename);
  53.     complain_table = read_complaint_file(_filename);
  54.  
  55. }  /* end of complaint_dict::complaint_dict() */
  56.  
  57. complaint_dict::~complaint_dict(void)
  58. {
  59.     /* cleanup - delete all of the offset records in the table. */
  60.  
  61.     complain_ptr *keyptr;
  62.  
  63.     free(_filename);
  64.     while ((keyptr = complain_table) != 0) {
  65.         complain_table = complain_table->next;
  66.         delete keyptr;
  67.     }
  68.  
  69. }  /* end of complaint_dict::~complaint_dict() */
  70.  
  71. int complaint_dict::key_defined(const char *name) const
  72. {
  73.     /* return 1 if the key is defined in this dictionary. */
  74.  
  75.     return key(complain_table,name) != 0;
  76.  
  77. }  /* end of complaint_dict::key_defined() */
  78.  
  79. static complain_ptr *key(complain_ptr *complain_table,const char *name)
  80. {
  81.     /* look for the key in the list of complain_ptr objects. in a */
  82.     /* production system this would be a hashed table. */
  83.  
  84.     while (complain_table != 0 && strcmp(name,complain_table->errname()))
  85.         complain_table = complain_table->next;
  86.     return complain_table;
  87.  
  88. }  /* end of key() */
  89.  
  90. int complaint_dict::complaint_text(const char *name,char *line,
  91.                                    int linelen) const
  92. {
  93.     /* store the text of the error message indexed by "name" (if found) */
  94.     /* into the buffer. returns a "not found" message in the line if */
  95.     /* something goes wrong. returns 0 on failure. */
  96.  
  97.     complain_ptr *fmt;
  98.  
  99.     fmt = key(complain_table,name);
  100.  
  101.     /* if no key, return a "key not found" message (in English; sorry). */
  102.     /* we assume there are no "%" signs in the key and that there is room */
  103.     /* in the buffer for the extra text. really the buffer should be */
  104.     /* an object that grows as we add text. */
  105.  
  106.     if (fmt == 0) {
  107.         sprintf(line,"Error key \"%s\" not found\n",key);
  108.         return 0;                       /* failed */
  109.     }
  110.  
  111.     /* if the file has been moved or mangled, say something to that effect. */
  112.  
  113.     if (fmt->complaint_text(_filename,line,linelen)) {
  114.         sprintf(line,"Unable to re-read text for error key \"%s\"\n",key);
  115.         return 0;                       /* failed */
  116.     }
  117.  
  118.     return 1;                           /* all OK */
  119.  
  120. }  /* end of complaint_dict::complaint_text() */
  121.  
  122. /************************** local utility routines **************************/
  123.  
  124. static complain_ptr *read_complaint_file(const char *filename)
  125. {
  126.     /* read all of the error keys in the complaint file. this is called */
  127.     /* from the dictionary's constructor, so we have no way to signal an */
  128.     /* error return. if the file isn't found, then no key will exist. */
  129.     /* if an error is found partway through, some keys will exist. in */
  130.     /* either case err_mgr.error() is called (which may cause execution */
  131.     /* to stop, per user request). */
  132.  
  133.     /* line format: "key_name: text" where the text goes to the end of */
  134.     /* the line. leading blanks after the ':' are skipped. if the last */
  135.     /* character is a '\', the following line is added to the text (the */
  136.     /* '\' is removed). */
  137.  
  138.     FILE *complaintfile;
  139.     char line[512];
  140.     long thisoffset;
  141.     char *keyname,*p,*name;
  142.     int all_ok = 1;
  143.     complain_ptr *keyptr,*complain_table = 0;
  144.  
  145.     if ((complaintfile = fopen(filename,"r")) == 0) {
  146.         err_mgr.error("Unable to read error text file %s\n",
  147.                       filename);
  148.         return 0;                       /* in case user wants to continue */
  149.     }
  150.  
  151.     for (;;) {                          /* exit from within */
  152.         thisoffset = ftell(complaintfile);  /* start of line */
  153.         read_continued_line(complaintfile,line,sizeof(line));
  154.         if (strlen(line) == 0)          /* EOF? done */
  155.             break;
  156.  
  157.         /* skip blank lines and comments. */
  158.  
  159.         keyname = p = line + skipblanks(line);
  160.         if (!*p || *p == '\n' || *p == '#')
  161.             continue;
  162.         p += skip_ident(p);             /* get key name */
  163.  
  164.         /* format errors within the file are considered only warnings; the */
  165.         /* user will want to see all of them and not be asked whether to */
  166.         /* continue after every error! */
  167.  
  168.         if (p == keyname) {
  169.             err_mgr.warn("Missing error key in error text file \"%s\":\n%s",
  170.                          filename,line);  /* \n in buf */
  171.             all_ok = 0;
  172.             continue;
  173.         }
  174.  
  175.         name = newstring(keyname,p - keyname);
  176.         if (key(complain_table,name) != 0) {
  177.             err_mgr.warn("Duplicate error key in error text file \"%s\":\n%s",
  178.                          filename,line);  /* \n in buf */
  179.             free(name);
  180.             all_ok = 0;
  181.             continue;
  182.         }
  183.         p += skipblanks(p);
  184.         if (*p != ':') {
  185.             err_mgr.warn("Missing ':' in error text file \"%s\":\n%s",
  186.                          filename,line);
  187.             free(name);
  188.             all_ok = 0;
  189.             continue;
  190.         }
  191.  
  192.         /* everything looks good - remember where the key was. */
  193.  
  194.         keyptr = new complain_ptr(name,thisoffset,complain_table);
  195.         complain_table = keyptr;
  196.     }  /* end of for(ever) */
  197.  
  198.     fclose(complaintfile);
  199.     if (!all_ok) {
  200.         while ((keyptr = complain_table) != 0) {
  201.             complain_table = complain_table->next;
  202.             delete keyptr;
  203.         }
  204.         return 0;
  205.     }
  206.     return complain_table;
  207.  
  208. }  /* end of read_complaint_file() */
  209.  
  210. /**************************** class complain_ptr ****************************/
  211.  
  212. complain_ptr::complain_ptr(char *name,long offset,complain_ptr *head)
  213. {                                       /* constructor */
  214.     _errname = name;                    /* allocated for us; we must delete */
  215.     foffset = offset;
  216.     next = head;                        /* add ourselves to front of list */
  217.  
  218. }  /* end of complain_ptr::complain_ptr() */
  219.  
  220. complain_ptr::~complain_ptr(void)       /* destructor */
  221. {
  222.     free(_errname);                     /* caller cleans up chain in next */
  223.  
  224. }  /* end of complain_ptr::~complain_ptr() */
  225.  
  226. int complain_ptr::complaint_text(const char *filename,char *line,int linelen)
  227. {
  228.     /* re-read the complaint text for the key. note that newlines are left */
  229.     /* in the text so that our caller need not supply a terminating newline. */
  230.  
  231.     /* returns 1 on error (file has disappeared or been edited). this is an */
  232.     /* internal routine, so we depend on our caller to print a useful error */
  233.     /* message. */
  234.  
  235.     FILE *complaintfile;
  236.     char *keyname,*p;
  237.     int newlen;
  238.  
  239.     line[0] = '\0';                     /* clear old text */
  240.     if ((complaintfile = fopen(filename,"r")) == 0)
  241.         return 1;
  242.     if (fseek(complaintfile,foffset,SEEK_SET)) {
  243.         fclose(complaintfile);
  244.         return 1;
  245.     }
  246.     read_continued_line(complaintfile,line,linelen);
  247.     fclose(complaintfile);
  248.  
  249.     /* make sure we still have the same key! (file may have been edited) */
  250.  
  251.     keyname = p = line + skipblanks(line);
  252.     p += skip_ident(p);                 /* get key name */
  253.     if (p == keyname || strncmp(keyname,_errname,strlen(_errname)))
  254.         return 1;
  255.     p += skipblanks(p);
  256.     if (*p != ':')
  257.         return 1;
  258.     ++p;                                /* skip ':', leading blanks */
  259.     p += skipblanks(p);
  260.  
  261.     /* OK, now we need to return only the error text, not the key at the */
  262.     /* front of it. shift everything over to cover the key. */
  263.  
  264.     newlen = strlen(p);
  265.     memmove(line,p,newlen + 1);
  266.     return 0;                           /* all OK */
  267.  
  268. }  /* end of complain_ptr::complaint_text() */
  269.